package main

import (
	"fmt"
	"math/rand"
	"strconv"
	"time"
)

func main() {
	dh := &DepositBusinessHandler{userVip: false}
	bbe := NewBankBusinessExecutor(dh)
	bbe.ExecuteBankBusiness()

	wd := &WithDrawBusiness{userVip: true}
	wbe := NewBankBusinessExecutor(wd)
	wbe.ExecuteBankBusiness()
}

/*
模版模式，有的也翻译成模版方法模式
当要做一件事儿的时候，这件事儿的流程和步骤是固定好的
但是每一个步骤的具体实现方式是不一定的。这个时候就可以使用模板模式。
*/

type BankBusinessHandler interface {
	// TakeRowNumber 排队取号
	TakeRowNumber()
	// WaitInHead 等位
	WaitInHead()
	// HandleBusiness 处理具体业务
	HandleBusiness()
	// Commentate 对服务做出评价
	Commentate()
	// CheckVipIdentity 钩子方法
	//用于在流程里判断是不是VIP 实现类似VIP不用等位的需求
	CheckVipIdentity() bool
}

// BankBusinessExecutor 完整业务执行类
type BankBusinessExecutor struct {
	handler BankBusinessHandler
}

// ExecuteBankBusiness 模板方法,所有的子类的执行都是这个流程
func (b *BankBusinessExecutor) ExecuteBankBusiness() {
	b.handler.TakeRowNumber()
	if !b.handler.CheckVipIdentity() {
		b.handler.WaitInHead()
	}

	b.handler.HandleBusiness()
	b.handler.Commentate()
}

// NewBankBusinessExecutor 通过传入具体的实现到模板类中形成每个独立的业务类
func NewBankBusinessExecutor(businessHandler BankBusinessHandler) *BankBusinessExecutor {
	return &BankBusinessExecutor{handler: businessHandler}
}

// DefaultBusinessHandler 默认的基础流程 所有业务的通用流程可以抽取出来封装，通过组合的方式继承下来
type DefaultBusinessHandler struct {
}

func (*DefaultBusinessHandler) TakeRowNumber() {
	fmt.Println("请拿好您的号码：" + strconv.Itoa(rand.Intn(100)) + ", 注意排队情况，过号后顺延三个安排")
}

func (*DefaultBusinessHandler) WaitInHead() {
	fmt.Println("排队等号中。。。。。")
	time.Sleep(5 * time.Second)
	fmt.Println("请去窗口XXX")
}

func (*DefaultBusinessHandler) Commentate() {
	fmt.Println("请对我的服务作出评价，满意请按0，满意请按0，(～￣▽￣)～")
}

func (*DefaultBusinessHandler) CheckVipIdentity() bool {
	// 留给具体实现类实现
	return false
}

// DepositBusinessHandler 具体的存钱业务类
type DepositBusinessHandler struct {
	*DefaultBusinessHandler
	userVip bool
}

func (*DepositBusinessHandler) HandleBusiness() {
	fmt.Println("账户存储很多万人民币...")
}

// CheckVipIdentity 通用的方法还可以抽象到BaseBusinessHandler里，组合到具体实现类里，减少重复代码（实现类似子类继承抽象类的效果）
func (dh *DepositBusinessHandler) CheckVipIdentity() bool {
	return dh.userVip
}

// 取钱业务类
type WithDrawBusiness struct {
	*DefaultBusinessHandler
	userVip bool
}

func (wd *WithDrawBusiness) HandleBusiness() {
	fmt.Println("账户取走很多万人民币...")
}

func (wd *WithDrawBusiness) CheckVipIdentity() bool {
	return wd.userVip
}

/*模板方法模式的缺点
由于继承关系自身的缺点，如果父类添加新的抽象方法，则所有子类都要改一遍。

*/
